#include"../../inc/net/EventLoop.h"
#include<sys/epoll.h>
#include<sys/eventfd.h>
#include<vector>
#include<functional>
#include<map>
#include<memory>
#include<assert.h>
#include<unistd.h>

namespace SE
{
    EventLoop::EventLoop(Acceptor& acc)
    : _epfd(createEpollfd()), _eventfd(createEventfd()), _acceptor(acc), _isLoop(false), _mutex(), _events(1024), _funcs(), _connections()
    {
        add(_acceptor.fd());
        add(_eventfd);
    }

    int EventLoop::createEpollfd()
    {
        int ret = epoll_create(1);
        if(-1 == ret)
            logError("epoll_create");

        return ret;
    }

    int EventLoop::createEventfd()
    {
        int ret = ::eventfd(0, 0);
        if(-1 == ret)
            logError("eventfd");

        return ret;
    }

    void EventLoop::wait()
    {
        int nready;
        do
        {
            nready = epoll_wait(_epfd, &_events[0], _events.size(), 5000);
        } while (-1 == nready);

        if(-1 == nready)
        {
            logError("epoll_wait");
            return ;
        }
        else if(0 == nready)
            std::cout << ">> epoll_wait timeout..." << std::endl;
        else
        {
            if(nready == static_cast<int>(_events.size()))
                _events.resize(2*nready);
            
            for(int i = 0; i != nready; i++)
            {
                int fd = _events[i].data.fd;
                if(_events[i].events & EPOLLIN)
                {
                    if(fd == _acceptor.fd())
                        handleConnection();
                    else if(fd == _eventfd)
                    {
                        handleRead();
                        doPendingFuncs();
                    }
                    else 
                        handleMessage(fd);
                }
            }
        }   
    }

    void EventLoop::Loop()
    {
        if(!_isLoop)
            _isLoop = true;
        while(_isLoop)
            wait();
    }

    void EventLoop::Unloop()
    {
        if(_isLoop)
            _isLoop = false;
    }

    void EventLoop::runInLoop(Func&& cb)
    {
        {
            MutexGuard lock(_mutex);
            _funcs.push_back(std::move(cb));
        }
        wake();
    }

    void EventLoop::handleConnection()
    {
        int peerfd = _acceptor.Cfd();
        TcpConnectionPtr connptr(new TcpConnection(peerfd, this));
        connptr->setConnectCB(_cb_connection);
        connptr->setMessageCB(_cb_msg);
        connptr->setCloseCB(_cb_close);

        _connections.insert(std::make_pair(peerfd, connptr));
        add(peerfd);

        connptr->handleConnectCB();
    }

    void EventLoop::handleMessage(int fd)
    {
        bool isClosed = isQuit(fd);
        TcpConnectionPtr p = _connections.at(fd);
        if(!p.get())
            logError("TcpConnectionPtr Err");

        if(isClosed)
        {
            del(fd);
            p->handleCloseCB();
            _connections.erase(fd);
        }
        else 
            p->handleMessageCB();
    }

    void EventLoop::add(int fd)
    {
        epoll_event ev;
        ev.data.fd = fd;
        ev.events = EPOLLIN;
        int ret = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
        if(-1 == ret)
            logError("epoll_ctl_add");
    }

    void EventLoop::del(int fd)
    {
        epoll_event ev;
        ev.data.fd = fd;
        ev.events = EPOLLIN;
        int ret = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &ev);
        if(-1 == ret)
            logError("epoll_ctl_del");
    }

    bool EventLoop::isQuit(int fd)
    {
        int ret;
        char buf;
        do
        {
            ret = ::recv(fd, &buf, 1, MSG_PEEK);
        } while (-1 == ret && errno == EINTR);

        if(errno)
            puts(strerror(errno));

        return (0 == ret || -1 == ret);
    }

    void EventLoop::handleRead()
    {
        uint64_t ev_read;
        int ret = ::read(_eventfd, &ev_read, sizeof(ev_read));
        if(ret != sizeof(ret))
            logError("handleRead");
    }

    void EventLoop::wake()
    {
        uint64_t ev_write;
        int ret = ::write(_eventfd, &ev_write, sizeof(ev_write));
        if(ret != sizeof(ret))
            logError("wake");
    }

    void EventLoop::doPendingFuncs()
    {
        vector<Func> temp;
        {
            MutexGuard lock(_mutex);
            temp.swap(_funcs);
        }

        for(auto & f : temp)
            f();
    }

    void EventLoop::setConnectCB(const TcpConnectionCallBack& cb)
    {
        _cb_connection = std::move(cb);
    }

    void EventLoop::setMessageCB(const TcpConnectionCallBack& cb)
    {
        _cb_msg = std::move(cb);
    }

    void EventLoop::setCloseCB(const TcpConnectionCallBack& cb)
    {
        _cb_close = std::move(cb);
    }
}
